package de.steric.battlemon.collision;

import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;

import de.steric.MathHelper;
import de.steric.battlemon.GameObject;

public class Collider {
	/**
	 * 0=bottom left back 1=bottom right back 2=bottom right front 3=bottom left
	 * front 4=top left back 5=top right back 6=top right front 7=top left front
	 */
	// bounding Vertices
	private Vector3f[] boundingVertices;
	// bounding Vertices Not Transformed
	private Vector3f[] bVerticesNT;

	private GameObject parent;

	/**
	 * 
	 * @param parent
	 *            Gameobject the collider belongs to
	 * @param RTF
	 *            local Position of the right top front Corner
	 * @param LBB
	 *            local Position of the left bottom back Corner
	 */
	public Collider(GameObject parent, Vector3f RTF, Vector3f LBB) {
		this.parent = parent;
		boundingVertices = new Vector3f[8];
		createCollider(RTF, LBB);
	}

	public boolean isCollision(Collider other) {
		if (checkCollision(other, 'x') && checkCollision(other, 'y')
				&& checkCollision(other, 'z')) {
			return true;
		}
		return false;
	}

	private boolean checkCollision(Collider other, char coord) {

		boolean isCollision = false;
		float maxT = 0;
		float minT = 0;
		float maxO = 0;
		float minO = 0;

		switch (coord) {
		case 'x':
			maxT = boundingVertices[1].x;
			minT = boundingVertices[1].x;
			maxO = other.boundingVertices[1].x;
			minO = other.boundingVertices[1].x;
			// get This X maximum
			for (int i = 0; i < 8; i++) {
				maxT = Math.max(boundingVertices[i].x, maxT);
				minT = Math.max(boundingVertices[i].x, minT);
			}

			// get Other X maximum
			for (int i = 0; i < 8; i++) {
				maxO = Math.max(other.boundingVertices[i].x, maxO);
				minO = Math.max(other.boundingVertices[i].x, minO);
			}

			if (maxT < maxO && maxT > minO) {
				isCollision = true;
			}

			if (minT < maxO && minT > minO) {
				isCollision = true;
			}

			if (minT == minO || maxT == maxO || minT == maxO || maxT == minO) {
				isCollision = true;
			}

			return isCollision;
		case 'y':
			maxT = boundingVertices[1].y;
			minT = boundingVertices[1].y;
			maxO = other.boundingVertices[1].y;
			minO = other.boundingVertices[1].y;
			// get This Y maximum
			for (int i = 0; i < 8; i++) {
				maxT = Math.max(boundingVertices[i].y, maxT);
				minT = Math.max(boundingVertices[i].y, minT);
			}

			// get Other Y maximum
			for (int i = 0; i < 8; i++) {
				maxO = Math.max(other.boundingVertices[i].y, maxO);
				minO = Math.max(other.boundingVertices[i].y, minO);
			}

			if (maxT < maxO && maxT > minO) {
				isCollision = true;
			}

			if (minT < maxO && minT > minO) {
				isCollision = true;
			}

			if (minT == minO || maxT == maxO || minT == maxO || maxT == minO) {
				isCollision = true;
			}

			return isCollision;
		case 'z':
			maxT = boundingVertices[1].z;
			minT = boundingVertices[1].z;
			maxO = other.boundingVertices[1].z;
			minO = other.boundingVertices[1].z;
			// get This Z maximum
			for (int i = 0; i < 8; i++) {
				maxT = Math.max(boundingVertices[i].z, maxT);
				minT = Math.max(boundingVertices[i].z, minT);
			}

			// get Other Z maximum
			for (int i = 0; i < 8; i++) {
				maxO = Math.max(other.boundingVertices[i].z, maxO);
				minO = Math.max(other.boundingVertices[i].z, minO);
			}

			if (maxT < maxO && maxT > minO) {
				isCollision = true;
			}

			if (minT < maxO && minT > minO) {
				isCollision = true;
			}

			if (minT == minO || maxT == maxO || minT == maxO || maxT == minO) {
				isCollision = true;
			}

			return isCollision;
		default:
			return false;
		}
	}

	/**
	 * creates an Collider
	 * 
	 * @param RTF
	 *            local Position of the right top front Corner
	 * @param LBB
	 *            local Position of the left bottom back Corner
	 */
	private void createCollider(Vector3f RTF, Vector3f LBB) {
		bVerticesNT = new Vector3f[8];

		// Fill the Array of not Transformed Vertices
		bVerticesNT[0] = LBB;
		bVerticesNT[1] = new Vector3f(RTF.x, LBB.y, LBB.z);
		bVerticesNT[2] = new Vector3f(RTF.x, LBB.y, RTF.z);
		bVerticesNT[3] = new Vector3f(LBB.x, LBB.y, RTF.z);
		bVerticesNT[4] = new Vector3f(LBB.x, RTF.y, LBB.z);
		bVerticesNT[5] = new Vector3f(RTF.x, RTF.y, LBB.z);
		bVerticesNT[6] = RTF;
		bVerticesNT[7] = new Vector3f(LBB.x, RTF.y, RTF.z);

		Matrix4f TransformationMatrix = MathHelper.getRotation(parent.getRotation());

		// Apply Rotation
		for (int i = 0; i < 8; i++) {
			Vector4f temp = new Vector4f(bVerticesNT[i].x, bVerticesNT[i].y,
					bVerticesNT[i].z, 1);
			Vector4f temp2 = Matrix4f.transform(TransformationMatrix, temp,
					null);
			boundingVertices[i] = new Vector3f(temp2.x, temp2.y, temp2.z);
		}

		// Apply translate
		for (int i = 0; i < 8; i++) {
			Vector3f.add(boundingVertices[i], parent.getPosition(),
					boundingVertices[i]);
		}
	}

	public Vector3f getboundingVerticeAt(int index){
		return boundingVertices[index];
	}
	
	public void setboundingVerticeAt(int index, Vector3f newVal){
		boundingVertices[index]=newVal;
	}
}
